package com.csx.base.concurrency.atomic;

import com.csx.base.concurrency.util.TimeUtil;

import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * <p> what does this class do ? </p>
 *   该案例包含 AtomicReference 引用类型原子类的使用
 *   描述:
 *     该案例以一个 AtomicReference 类型的原子引用变量作为目标
 *     启动了3个线程对该变量进行更新。其中两个线程同时启动，另外一个线程等待一会后启动。
 *     率先启动的两个线程最终只有一个线程满足更新条件更新成功。最后一个启动的线程在等待一会后也满足更新条件从而成功更新
 * <p> How does it work ?</p>
 * <p> Why does it do this ?</p>
 *
 * @author cuisongxu
 * @date 2024/12/3 周二 9:26
 */
public class AtomicReferenceTest {

    private final AtomicReference<Config> configAtomicReference = new AtomicReference<>(new Config("xxxx", new Date() {
        {
            // 匿名内部类方式创建Date对象
            Calendar calendar = Calendar.getInstance();
            calendar.set(2024, Calendar.DECEMBER, 3, 10, 30, 0); // 设置为 2024 年 12 月 3 日 15:30:00
            this.setTime(calendar.getTimeInMillis()); // 返回 Date 对象
        }

    }));


    // 入口方法
    public void test() {
        ConfigUpdater configUpdater = new ConfigUpdater();
        Thread thread1 = new Thread(() -> configUpdater.updateConfig("111"), "thread1");
        Thread thread2 = new Thread(() -> configUpdater.updateConfig("222"), "thread2");
        Thread thread3 = new Thread(() -> configUpdater.updateConfig("333"), "thread3");

        try {
            thread1.start();
            thread2.start();
            // 休眠2s确保此时 thread1 和 thread2 已经成功执行完毕
            TimeUnit.SECONDS.sleep(2);
            System.out.println("更新后的配置: " + configAtomicReference.get());
            // 继续等待4s,让config配置超时,此时thread3就能更新config
            TimeUnit.SECONDS.sleep(4);
            thread3.start();

            thread1.join();
            thread2.join();
            thread3.join();

        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("最终的配置: " +configAtomicReference.get());
    }

    class ConfigUpdater {

        public void updateConfig(String newConfigInfo) {

            Config oldConfig;
            Config newConfig;
            boolean isIn10Seconds;

            do {
                oldConfig = configAtomicReference.get();
                Date now = new Date();
                newConfig = new Config(newConfigInfo, now);
                // 配置更新时间是否在5s内
                isIn10Seconds = now.getTime() - oldConfig.getUpdateTime().getTime() <= 5000;
                // 如果在5s之内,则直接退出,不更新
                if(isIn10Seconds) {
                    String nowTimeStr = TimeUtil.getNowTimeStr();
                    System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + " 由于配置在5s内已经被更新过因此更新配置失败");
                    break;
                }
            }while (!configAtomicReference.compareAndSet(oldConfig, newConfig));

        }
    }


    public static void main(String[] args) {
        AtomicReferenceTest atomicReferenceTest = new AtomicReferenceTest();
        atomicReferenceTest.test();
    }

    static class Config {
        private String configInfo;

        private Date updateTime;

        public Config(String configInfo, Date updateTime) {
            this.configInfo = configInfo;
            this.updateTime = updateTime;
        }

        public String getConfigInfo() {
            return configInfo;
        }

        public void setConfigInfo(String configInfo) {
            this.configInfo = configInfo;
        }

        public Date getUpdateTime() {
            return updateTime;
        }

        public void setUpdateTime(Date updateTime) {
            this.updateTime = updateTime;
        }

        @Override
        public String toString() {
            return "Config{" +
                    "configInfo='" + configInfo + '\'' +
                    ", updateTime=" + updateTime +
                    '}';
        }
    }
}
